/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { BaseChecker, BaseMetaData } from '../BaseChecker';
import {
    ClassMatcher,
    Defects,
    IssueReport,
    MatcherCallback,
    MatcherTypes,
    Rule,
    ViewTreeNodeMatcher
} from '../../Index';
import { Stmt, ViewTreeNode } from 'arkanalyzer';
import { ArkClass } from 'arkanalyzer/lib/core/model/ArkClass';
import { CompositeMatcher } from '../../matcher/matcherAdapter/matchCommon';

const gMetaData: BaseMetaData = {
    severity: 1,
    ruleDocPath: 'docs/component-degree-check.md',
    description: 'Recommended not to exceed 60 child nodes.'
};

const LIMIT_SIZE = 60;

export class ComponentDegreeCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];

    private clsMatcher: ClassMatcher = {
        matcherType: MatcherTypes.CLASS,
        match(node: ArkClass): boolean {
            return node.hasEntryDecorator();
        }
    };

    private vtnMatcher: ViewTreeNodeMatcher = {
        matcherType: MatcherTypes.VIEW_TREE_NODE,
        match(node: ViewTreeNode): boolean {
            return node.children.length > LIMIT_SIZE;
        }
    };

    public registerMatchers(): MatcherCallback[] {
        const matchMethodCb: MatcherCallback = {
            matcher: new CompositeMatcher([this.clsMatcher, this.vtnMatcher]),
            callback: this.check
        };
        return [matchMethodCb];
    }

    public check = (node: ViewTreeNode): void => {
        const stmt = node.attributes.get('create')?.[0];
        if (stmt) {
            this.reportIssue(stmt, node.name.length);
        }
    };


    private reportIssue(stmt: Stmt, length: number): void {
        const filePath = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile().getFilePath();
        let fullPosition = stmt.getOperandOriginalPosition(0);
        if (!fullPosition) {
            return;
        }
        let lineNum = fullPosition.getFirstLine();
        let startColumn = fullPosition.getFirstCol();
        let endColumn = startColumn + length;
        if (lineNum === -1 || startColumn === -1) {
            return;
        }
        const severity = this.rule.alert ?? this.metaData.severity;
        let defects = new Defects(lineNum, startColumn, endColumn, this.metaData.description, severity,
            this.rule.ruleId, filePath, this.metaData.ruleDocPath, true, false, false);
        this.issues.push(new IssueReport(defects, undefined));
    }
}